﻿using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace PocoGen.Common
{
    /// <summary>
    /// Represents a column in a table.
    /// </summary>
    public class Column : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Creates a new column.
        /// </summary>
        /// <param name="table">The column's table.</param>
        /// <param name="name">The column's name.</param>
        public Column(Table table, string name)
        {
            if (table == null)
            {
                throw new ArgumentNullException("table", "table is null.");
            }

            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentException("name is null or empty.", "name");
            }

            this.Name = name;
            this.Table = table;
            this.GeneratedPropertyName = string.Empty;
            this.PropertyType = null;
            this.IsPK = false;
            this.IsNullable = false;
            this.IsAutoIncrement = false;
            this.Ignore = false;
        }

        /// <summary>
        /// Creates a new column.
        /// </summary>
        /// <param name="table">The column's table.</param>
        /// <param name="name">The column's name.</param>
        /// <param name="propertyType">The column's property type.</param>
        /// <param name="isNullable">Whether the column can be null.</param>
        /// <param name="isAutoIncrement">Whether the column is an identity column.</param>
        public Column(Table table, string name, ColumnBaseType propertyType, bool isNullable, bool isAutoIncrement)
            : this(table, name)
        {
            if (propertyType == null)
            {
                throw new ArgumentNullException("propertyType", "propertyType is null.");
            }

            this.PropertyType = propertyType;
            this.IsNullable = isNullable;
            this.IsAutoIncrement = isAutoIncrement;
        }

        /// <summary>
        /// Gets the column's table.
        /// </summary>
        public Table Table { get; private set; }

        /// <summary>
        /// Gets or sets the column name in the database.
        /// </summary>
        public string Name { get; private set; }

        private string generatedPropertyName;
        /// <summary>
        /// Gets or sets the property name which was generated by the column name generators.
        /// </summary>
        public string GeneratedPropertyName
        {
            get
            {
                return this.generatedPropertyName;
            }
            set
            {
                if (this.generatedPropertyName == value)
                {
                    return;
                }

                this.generatedPropertyName = value;
                this.OnPropertyChanged();
                this.OnPropertyChanged(nameof(this.EffectivePropertyName));
            }
        }

        private string userChangedPropertyName;
        /// <summary>
        /// Gets or sets the effective property name. This is the <see cref="GeneratedPropertyName"/> if the user didn't change it, otherwise it is the user-changed property name.
        /// </summary>
        public string EffectivePropertyName
        {
            get
            {
                // If the user changed the property name, it was saved to userChangedPropertyName. Otherwise it is the GeneratedPropertyName.
                return this.userChangedPropertyName ?? this.GeneratedPropertyName;
            }
            set
            {
                if (this.EffectivePropertyName == value)
                {
                    return;
                }

                // If the user changes the property name, save the override in userChangedPropertyName.
                if (this.GeneratedPropertyName != value)
                {
                    this.userChangedPropertyName = value;
                }
                else
                {
                    this.userChangedPropertyName = null;
                }

                this.OnPropertyChanged();
                this.OnPropertyChanged(nameof(this.UserChangedPropertyName));
            }
        }

        /// <summary>
        /// Gets the changed property name if the user changed it, otherwise null.
        /// </summary>
        public string UserChangedPropertyName
        {
            get
            {
                return this.userChangedPropertyName;
            }
        }

        private ColumnBaseType propertyType;
        /// <summary>
        /// Gets or sets the property's type.
        /// </summary>
        public ColumnBaseType PropertyType
        {
            get
            {
                return this.propertyType;
            }
            set
            {
                if (this.propertyType == value)
                {
                    return;
                }

                this.propertyType = value;
                this.OnPropertyChanged();
            }
        }

        private bool isPK;
        /// <summary>
        /// Gets or sets whether this column is part of a primary key.
        /// </summary>
        public bool IsPK
        {
            get
            {
                return this.isPK;
            }
            set
            {
                if (this.isPK == value)
                {
                    return;
                }

                this.isPK = value;
                this.OnPropertyChanged();
            }
        }

        private bool isNullable;
        /// <summary>
        /// Gets or sets whether this class is nullable.
        /// </summary>
        public bool IsNullable
        {
            get
            {
                return this.isNullable;
            }
            set
            {
                if (this.isNullable == value)
                {
                    return;
                }

                this.isNullable = value;
                this.OnPropertyChanged();
            }
        }

        private bool isAutoIncrement;
        /// <summary>
        /// Gets or sets whether this column is an identity column.
        /// </summary>
        public bool IsAutoIncrement
        {
            get
            {
                return this.isAutoIncrement;
            }
            set
            {
                if (this.isAutoIncrement == value)
                {
                    return;
                }

                this.isAutoIncrement = value;
                this.OnPropertyChanged();
            }
        }

        private bool ignore;
        /// <summary>
        /// Gets or sets whether this column should be included in the POCO.
        /// </summary>
        public bool Ignore
        {
            get
            {
                return this.ignore;
            }
            set
            {
                if (this.ignore == value)
                {
                    return;
                }

                this.ignore = value;
                this.OnPropertyChanged();
            }
        }

        private void OnPropertyChanged([CallerMemberName]string propertyName = null)
        {
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}